#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
渗透测试工具 - 登录界面安全检测 (含验证码识别版)
版本: 2.3
兼容: Kali Linux 2025.2
更新: 增强验证码检测证据展示
作者: 来自于狂人（CSDN博客同名）
"""

import os
import sys
import requests
import urllib3
import subprocess
import re
import time
import random
import datetime
from bs4 import BeautifulSoup
from urllib.parse import urljoin, urlparse
import hashlib

# 禁用SSL证书验证警告
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)

# 全局配置
USER_AGENTS = [
    "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
    "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
    "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/119.0",
    "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"
]

# 字典文件路径
WEAK_PASS_PATH = "/usr/share/wordlists/rockyou.txt"
WEAK_PASS_GZ_PATH = "/usr/share/wordlists/rockyou.txt.gz"
USERNAMES_PATH = "/usr/share/seclists/Usernames/top-usernames-shortlist.txt"

# 技术栈特定成功条件字符串
SUCCESS_STRINGS_BY_TECH = {
    "PHP": ["dashboard", "welcome", "admin", "控制台", "登录成功", "redirect", "index.php"],
    "Vue": ["登录成功", "token", "administrator", "200", "dashboard", "home", "app.vue"],
    "React": ["true", "session", "admin@example.com", "welcome", "profile", "react-root"],
    "Java": ["OK", "ROLE_ADMIN", "authorities", "dashboard", "admin", ".do", ".action"],
    "Python": ["is_staff", "is_superuser", "success", "admin", "dashboard", "django"],
    ".NET": ["true", "Admin User", "token", "result", "dashboard", ".aspx"],
    "通用": ["success", "ok", "200", "true", "dashboard", "welcome", "home", "admin"]
}

# 技术栈特征
TECHNOLOGY_SIGNATURES = {
    "PHP": {
        "headers": ["x-powered-by: php", "server: php"],
        "cookies": ["phpsessid"],
        "html": ["php", "index.php"]
    },
    "Vue": {
        "html": ["vue.js", "vue.min.js", "vue-router", "vuex"],
        "headers": ["x-powered-by: vue"]
    },
    "React": {
        "html": ["react.js", "react.min.js", "react-dom", "create-react-app"],
        "headers": ["x-powered-by: react"]
    },
    "Java": {
        "headers": ["x-powered-by: servlet", "server: tomcat", "server: jetty", "server: jboss"],
        "cookies": ["jsessionid", "jsessionid"],
        "html": [".do", ".action", ".jsp"]
    },
    "Python": {
        "headers": ["server: gunicorn", "server: uwsgi", "server: django"],
        "cookies": ["sessionid", "csrftoken"],
        "html": ["django", "flask", "wsgi"]
    },
    ".NET": {
        "headers": ["x-aspnet-version", "x-powered-by: asp.net"],
        "cookies": ["asp.net_sessionid", "aspnetsessionid"],
        "html": [".aspx", ".ashx", ".asmx"]
    }
}

class LoginTester:
    def __init__(self, target_url):
        """
        初始化测试器
        :param target_url: 目标登录页面URL
        """
        self.target_url = target_url
        self.session = requests.Session()
        self.session.verify = False  # 禁用SSL证书验证
        self.session.headers.update({"User-Agent": random.choice(USER_AGENTS)})
        self.username_field = None
        self.password_field = None
        self.login_action = None
        self.csrf_token = None
        self.csrf_field = None
        self.redirect_url = None
        self.tech_stack = None
        self.found_credentials = []
        self.result_file = f"hydra_results_{datetime.datetime.now().strftime('%Y%m%d_%H%M%S')}.txt"
        self.base_response_cache = None  # 缓存未登录页面响应
        self.session_cookie = None
        self.has_captcha = False # 验证码标志位
        self.captcha_type = None # 验证码类型描述
        self.captcha_evidence = [] # 存储验证码检测证据
        
        # 解析目标URL获取主机和端口
        parsed_url = urlparse(target_url)
        self.host = parsed_url.hostname
        self.port = parsed_url.port or (443 if parsed_url.scheme == 'https' else 80)
        self.scheme = parsed_url.scheme
      
        # 获取基础页面信息
        self._get_base_page_info()
        # 自动检测登录表单
        self._detect_form()
        # 自动检测验证码（增强版）
        self._detect_captcha_enhanced()
        # 自动检测技术栈
        self._detect_tech_stack()
  
    def _get_base_page_info(self):
        """获取基础页面信息用于后续比较"""
        try:
            print("\n[*] 获取基础页面信息...")
            response = self.session.get(self.target_url, timeout=10, verify=False)
            self.base_response_cache = {
                'status_code': response.status_code,
                'content_length': len(response.content),
                'content_hash': hashlib.md5(response.content).hexdigest(),
                'headers': dict(response.headers),
                'cookies': dict(response.cookies),
                'text': response.text # 保存文本用于后续分析
            }
            print(f"[+] 基础页面状态码: {response.status_code}")
            print(f"[+] 基础页面长度: {len(response.content)} 字节")
        except Exception as e:
            print(f"[!] 获取基础页面信息失败: {str(e)}")
            self.base_response_cache = {}

    def _detect_captcha_enhanced(self):
        """增强版验证码检测，显示详细证据"""
        print("\n[*] 正在检测验证码机制...")
        print("-" * 60)
        
        if not self.base_response_cache or 'text' not in self.base_response_cache:
            print("[!] 无法获取页面内容进行检测")
            return

        html_content = self.base_response_cache['text']
        soup = BeautifulSoup(html_content, 'html.parser')
        
        # 重置证据列表
        self.captcha_evidence = []
        
        print("[检测维度1: 输入框字段名]")
        # 1. 检查input字段名
        captcha_input_keywords = ['captcha', 'verify', 'vcode', 'checkcode', 'yzm', 'code', 'authcode']
        inputs = soup.find_all('input')
        found_inputs = []
        for inp in inputs:
            name = inp.get('name', '').lower()
            if name and name not in [self.username_field, self.password_field, self.csrf_field]:
                if any(k in name for k in captcha_input_keywords):
                    # 排除 hidden 类型的 input，通常 hidden 是 token 而不是验证码
                    if inp.get('type') != 'hidden':
                        found_inputs.append(inp)
        
        if found_inputs:
            print(f"  [+] 发现 {len(found_inputs)} 个可疑的验证码输入框:")
            for idx, inp in enumerate(found_inputs, 1):
                print(f"    {idx}. 字段名: {inp.get('name')} | 类型: {inp.get('type', 'text')} | "
                      f"ID: {inp.get('id', '无')} | 占位符: {inp.get('placeholder', '无')}")
                # 显示HTML片段
                html_snippet = str(inp)[:100] + "..." if len(str(inp)) > 100 else str(inp)
                print(f"       HTML片段: {html_snippet}")
                self.captcha_evidence.append(f"输入框字段: {inp.get('name')}")
        else:
            print("  [-] 未发现可疑的验证码输入框")
        
        print("\n[检测维度2: 验证码图片]")
        # 2. 检查图片 src
        captcha_img_keywords = ['captcha', 'kaptcha', 'getcode', 'verify_code', 'image.jsp', 'servlet/captcha']
        imgs = soup.find_all('img')
        found_imgs = []
        for img in imgs:
            src = img.get('src', '').lower()
            alt = img.get('alt', '').lower()
            if any(k in src for k in captcha_img_keywords) or "验证码" in alt:
                found_imgs.append(img)
        
        if found_imgs:
            print(f"  [+] 发现 {len(found_imgs)} 个可疑的验证码图片:")
            for idx, img in enumerate(found_imgs, 1):
                print(f"    {idx}. SRC: {img.get('src')} | ALT: {img.get('alt', '无')} | "
                      f"ID: {img.get('id', '无')} | CLASS: {img.get('class', '无')}")
                # 显示HTML片段
                html_snippet = str(img)[:100] + "..." if len(str(img)) > 100 else str(img)
                print(f"       HTML片段: {html_snippet}")
                self.captcha_evidence.append(f"验证码图片: {img.get('src')}")
        else:
            print("  [-] 未发现可疑的验证码图片")
        
        print("\n[检测维度3: 第三方验证码服务]")
        # 3. 检查常见的第三方验证码脚本/iframe
        third_party_captchas = {
            'recaptcha': 'Google reCAPTCHA',
            'hcaptcha': 'hCaptcha',
            'geetest': '极验(Geetest)',
            'turnstile': 'Cloudflare Turnstile',
            'tencent-captcha': '腾讯防水墙'
        }
        
        found_third_party = []
        for keyword, name in third_party_captchas.items():
            if keyword in html_content.lower():
                found_third_party.append((keyword, name))
        
        if found_third_party:
            print(f"  [+] 发现 {len(found_third_party)} 个第三方验证码服务:")
            for idx, (keyword, name) in enumerate(found_third_party, 1):
                print(f"    {idx}. 服务: {name} | 关键字: {keyword}")
                # 显示匹配的代码片段
                pattern = re.compile(keyword, re.IGNORECASE)
                match = pattern.search(html_content)
                if match:
                    start = max(0, match.start() - 50)
                    end = min(len(html_content), match.end() + 50)
                    snippet = html_content[start:end]
                    print(f"       匹配片段: ...{snippet}...")
                self.captcha_evidence.append(f"第三方验证码: {name}")
        else:
            print("  [-] 未发现第三方验证码服务")
        
        print("\n[检测维度4: 页面文本提示]")
        # 4. 检查文本关键字
        text_keywords = ["验证码", "换一张", "看不清", "点击验证", "拖动滑块"]
        page_text = soup.get_text()
        found_texts = []
        for k in text_keywords:
            if k in page_text:
                found_texts.append(k)
        
        if found_texts:
            print(f"  [+] 发现 {len(found_texts)} 个验证码相关文本提示:")
            for idx, text in enumerate(found_texts, 1):
                # 查找文本在页面中的位置
                lines = page_text.split('\n')
                for line_num, line in enumerate(lines, 1):
                    if text in line:
                        print(f"    {idx}. 关键词: '{text}' | 行号: {line_num}")
                        print(f"       上下文: {line.strip()[:100]}...")
                        break
                self.captcha_evidence.append(f"页面文本提示: {text}")
        else:
            print("  [-] 未发现验证码相关文本提示")
        
        # 总结
        print("\n" + "="*60)
        print("验证码检测总结")
        print("="*60)
        if self.captcha_evidence:
            self.has_captcha = True
            self.captcha_type = ", ".join(list(set(self.captcha_evidence)))
            print(f"[!] 检测结果: 存在验证码机制")
            print(f"[!] 置信度: 高 (发现 {len(self.captcha_evidence)} 类证据)")
            print(f"\n[证据清单]:")
            for idx, evidence in enumerate(self.captcha_evidence, 1):
                print(f"    {idx}. {evidence}")
            print(f"\n[!] 警告: 存在验证码通常会导致自动化暴力破解失败。")
        else:
            print("[+] 检测结果: 未检测到明显的验证码机制")
            self.has_captcha = False
        print("="*60)

    def _detect_form(self):
        """自动检测登录表单参数"""
        try:
            print("\n[*] 正在检测登录表单...")
            # 如果已有缓存，直接使用，避免重复请求
            if self.base_response_cache and 'text' in self.base_response_cache:
                html_content = self.base_response_cache['text']
            else:
                response = self.session.get(self.target_url, timeout=10, verify=False)
                html_content = response.text

            soup = BeautifulSoup(html_content, 'html.parser')
          
            # 查找登录表单
            forms = soup.find_all('form')
            if not forms:
                raise ValueError("未找到登录表单！")
          
            # 优先选择包含密码输入的表单
            form = None
            for f in forms:
                if f.find('input', {'type': 'password'}):
                    form = f
                    break
          
            if not form:
                form = forms[0]  # 如果没有找到密码输入，使用第一个表单
              
            # 获取表单提交地址
            action = form.get('action', '')
            if not action.startswith(('http://', 'https://')):
                # 相对路径，转换为绝对路径
                self.login_action = urljoin(self.target_url, action)
            else:
                self.login_action = action
              
            # 检测用户名字段
            username_candidates = ["user", "username", "login", "email", "name", "account"]
            username_inputs = form.find_all('input', {'name': True})
            for inp in username_inputs:
                name = inp.get('name').lower()
                if any(candidate in name for candidate in username_candidates):
                    self.username_field = inp.get('name')
                    break
          
            # 检测密码字段
            password_inputs = form.find_all('input', {'type': 'password'})
            if password_inputs:
                self.password_field = password_inputs[0].get('name')
          
            # 检测CSRF令牌 - 更准确的检测
            csrf_candidates = ["csrf", "token", "_token", "authenticity_token", "xsrf_token", "anticsrf", "csrf_token"]
            csrf_inputs = form.find_all('input', {'name': True, 'type': ['hidden', 'text']})
            for inp in csrf_inputs:
                name = inp.get('name').lower()
                if any(candidate in name for candidate in csrf_candidates):
                    self.csrf_field = inp.get('name')
                    self.csrf_token = inp.get('value')
                    break
                  
            # 如果没有找到用户名字段，尝试使用第一个非密码文本输入
            if not self.username_field:
                for inp in username_inputs:
                    if inp.get('type') in ['text', 'email'] and inp.get('name') != self.password_field:
                        self.username_field = inp.get('name')
                        break
                      
            # 如果还是没有，使用第一个输入字段
            if not self.username_field and username_inputs:
                self.username_field = username_inputs[0].get('name')
              
            print(f"\n[+] 表单自动检测结果:")
            print(f"    - 提交地址: {self.login_action}")
            print(f"    - 用户名字段: {self.username_field}")
            print(f"    - 密码字段: {self.password_field}")
            print(f"    - CSRF字段: {self.csrf_field}")
            print(f"    - CSRF令牌: {'已检测' if self.csrf_token else '未检测'}")
          
        except Exception as e:
            print(f"[!] 表单检测失败: {str(e)}")
            return False
        return True

    def _detect_tech_stack(self):
        """检测目标技术栈"""
        try:
            print("\n[*] 正在检测技术栈...")
            # 使用缓存
            if not self.base_response_cache:
                response = self.session.get(self.target_url, timeout=10, verify=False)
                content = response.text.lower()
                headers = {k.lower(): v.lower() for k, v in response.headers.items()}
                cookies = {k.lower(): v.lower() for k, v in response.cookies.items()}
            else:
                content = self.base_response_cache['text'].lower()
                headers = {k.lower(): v.lower() for k, v in self.base_response_cache['headers'].items()}
                cookies = {k.lower(): v.lower() for k, v in self.base_response_cache['cookies'].items()}
          
            # 初始化匹配分数
            tech_scores = {tech: 0 for tech in TECHNOLOGY_SIGNATURES}
          
            # 检查每种技术栈的特征
            for tech, signatures in TECHNOLOGY_SIGNATURES.items():
                # 检查headers
                if "headers" in signatures:
                    for header in signatures["headers"]:
                        header_name, header_value = header.split(": ", 1) if ": " in header else (header, "")
                        if header_name in headers:
                            if not header_value or header_value in headers[header_name]:
                                tech_scores[tech] += 3
              
                # 检查cookies
                if "cookies" in signatures:
                    for cookie in signatures["cookies"]:
                        if cookie in cookies:
                            tech_scores[tech] += 2
              
                # 检查HTML内容
                if "html" in signatures:
                    for pattern in signatures["html"]:
                        if pattern in content:
                            tech_scores[tech] += 1
          
            # 找到得分最高的技术栈
            max_score = max(tech_scores.values())
            if max_score > 0:
                for tech, score in tech_scores.items():
                    if score == max_score:
                        self.tech_stack = tech
                        break
            else:
                self.tech_stack = "通用"
              
            print(f"\n[+] 检测到技术栈: {self.tech_stack} (得分: {max_score})")
            return True
          
        except Exception as e:
            print(f"[!] 技术栈检测失败: {str(e)}")
            self.tech_stack = "通用"
            return False

    def _dynamic_get_csrf_token(self):
        """动态获取CSRF令牌"""
        try:
            response = self.session.get(self.target_url, timeout=10, verify=False)
            soup = BeautifulSoup(response.text, 'html.parser')
            
            # 查找meta标签中的CSRF令牌
            csrf_meta = soup.find('meta', {'name': re.compile(r'csrf|token', re.I)})
            if csrf_meta and csrf_meta.get('content'):
                return csrf_meta.get('content')
            
            # 查找隐藏输入框中的CSRF令牌
            csrf_input = soup.find('input', {'name': re.compile(r'csrf|token', re.I)})
            if csrf_input and csrf_input.get('value'):
                return csrf_input.get('value')
                
            return None
        except:
            return None

    def _check_login_success_detailed(self, response, original_response=None):
        """详细检查登录成功状态"""
        # 检查重定向
        if response.history:
            # 检查是否重定向到预期的登录后页面
            final_url = response.url.lower()
            success_indicators = ["dashboard", "home", "admin", "welcome", "index", "profile", "logout", "main"]
            for indicator in success_indicators:
                if indicator in final_url:
                    return {
                        'success': True,
                        'reason': f'重定向到受保护页面: {response.url}',
                        'type': 'redirect'
                    }
        
        # 检查响应内容
        content = response.text.lower()
        
        # 检查成功标志
        success_indicators = ["dashboard", "welcome", "admin", "logout", "profile", "home", "控制台", "仪表盘", "登录成功"]
        for indicator in success_indicators:
            if indicator in content:
                return {
                    'success': True,
                    'reason': f'包含成功标志: {indicator}',
                    'type': 'content'
                }
        
        # 检查错误标志
        error_indicators = ["error", "invalid", "failed", "incorrect", "denied", "wrong", "失败", "错误", "不存在", "用户名或密码错误", "验证码错误", "code error", "captcha"]
        for indicator in error_indicators:
            if indicator in content:
                return {
                    'success': False,
                    'reason': f'包含错误标志: {indicator}',
                    'type': 'error_content'
                }
        
        # 比较响应与基础页面的差异
        if original_response:
            current_hash = hashlib.md5(response.content).hexdigest()
            if current_hash != self.base_response_cache.get('content_hash'):
                return {
                    'success': True,
                    'reason': '响应内容与基础页面不同',
                    'type': 'content_change'
                }
        
        # 检查状态码
        if response.status_code == 200:
            return {
                'success': True,
                'reason': '状态码200且无错误标志',
                'type': 'status_200'
            }
        
        return {
            'success': False,
            'reason': '未检测到成功标志',
            'type': 'no_success'
        }

    def test_empty_password(self):
        """测试空口令漏洞"""
        print("\n[*] 开始空口令测试...")
        
        if self.has_captcha:
            print(f"[!] 警告: 检测到验证码 ({self.captcha_type})。")
            print("    空口令测试发送的请求不包含验证码，可能会直接被服务器拒绝。")
            choice = input("    是否跳过空口令测试以避免触发安全警报? (y/n) [推荐y]: ").lower()
            if choice != 'n':
                print("[-] 已跳过空口令测试。")
                return False

        # 更全面的空口令测试样本
        test_users = [
            # 常见管理员账户
            "admin", "administrator", "root", 
            # 常见用户名
            "test", "guest", "user", "demo", "demo1", "demo2",
            # 系统用户
            "system", "service", "operator", "maint", "backup",
            # 随机生成的不存在用户
            "nonexistent_user_" + ''.join(random.choices('abcdefghijklmnopqrstuvwxyz0123456789', k=8))
        ]
        
        found = False
        print(f"    将测试 {len(test_users)} 个用户...")
        
        for i, user in enumerate(test_users, 1):
            print(f"    [{i}/{len(test_users)}] 测试用户: {user} (空密码)")
            
            # 动态获取CSRF令牌
            csrf_token = self._dynamic_get_csrf_token()
            if not csrf_token:
                csrf_token = self.csrf_token
            
            data = {
                self.username_field: user,
                self.password_field: ""
            }
            if self.csrf_field and csrf_token:
                data.update({self.csrf_field: csrf_token})
              
            try:
                response = self.session.post(self.login_action, data=data, timeout=10, verify=False)
                result = self._check_login_success_detailed(response)
                
                if result['success']:
                    print(f"[!] 空口令漏洞发现! 用户: {user} 密码: 空")
                    print(f"    原因: {result['reason']}")
                    self.found_credentials.append({"username": user, "password": "", "type": "空口令"})
                    found = True
                    break  # 发现漏洞后停止测试
            except Exception as e:
                print(f"[!] 请求失败: {str(e)}")
                continue
              
        if not found:
            print("[+] 未发现空口令漏洞")
        else:
            print(f"[+] 空口令测试完成，发现 {len([c for c in self.found_credentials if c['type'] == '空口令'])} 个漏洞")
        return found

    def test_weak_passwords(self):
        """测试弱口令漏洞"""
        print("\n[*] 开始弱口令测试...")
        
        if self.has_captcha:
            print(f"[!] 警告: 检测到验证码 ({self.captcha_type})。")
            print("    Python脚本无法自动识别验证码，发送的请求将缺少验证码字段或验证码错误。")
            print("    这会导致测试失败，并可能导致IP被封锁。")
            choice = input("    是否跳过弱口令测试? (y/n) [推荐y]: ").lower()
            if choice != 'n':
                print("[-] 已跳过弱口令测试。")
                return False

        # 更全面的用户测试样本
        test_users = [
            # 系统默认账户
            "admin", "administrator", "root", "Admin", "ADMIN", "ROOT",
            # 常见用户名
            "test", "guest", "user", "demo", "demo1", "demo2", "testuser",
            # 系统用户
            "system", "service", "operator", "maint", "backup", "webadmin",
            # 开发相关
            "developer", "dev", "developer1", "api", "service", "monitor",
            # 随机测试
            "user123", "test123", "admin123", "root123"
        ]
        
        # 更全面的弱密码测试样本
        weak_passwords = [
            # 常见弱密码
            "password", "Password", "PASSWORD", "123456", "12345678", "123456789",
            # 简单数字组合
            "000000", "111111", "222222", "333333", "444444", "555555", "666666",
            # 键盘序列
            "qwerty", "qwertyuiop", "asdfgh", "asdfghjkl", "zxcvbn", "zxcvbnm",
            # 重复字符
            "aaaaaa", "111111", "121212", "123123", "123321", "321321",
            # 个人信息
            "admin", "root", "test", "guest", "user", "demo", "backup",
            # 常见组合
            "welcome", "welcome123", "iloveyou", "admin123", "root123",
            "password123", "1234567890", "0987654321", "password1", "1q2w3e4r",
            "1qaz2wsx", "00000000", "1234qwer", "qwer1234", "123qwe",
            # 无密码
            "", " "
        ]
        
        found = False
        total_tests = len(test_users) * len(weak_passwords)
        print(f"    将测试 {len(test_users)} 个用户 x {len(weak_passwords)} 个密码 = {total_tests} 种组合")
        
        test_count = 0
        for user in test_users:
            for pwd in weak_passwords:
                test_count += 1
                print(f"    [{test_count}/{total_tests}] 测试组合: {user}:{pwd}")
                
                # 动态获取CSRF令牌
                csrf_token = self._dynamic_get_csrf_token()
                if not csrf_token:
                    csrf_token = self.csrf_token
                
                data = {
                    self.username_field: user,
                    self.password_field: pwd
                }
                if self.csrf_field and csrf_token:
                    data.update({self.csrf_field: csrf_token})
                  
                try:
                    response = self.session.post(self.login_action, data=data, timeout=10, verify=False)
                    result = self._check_login_success_detailed(response)
                    
                    if result['success']:
                        print(f"[!] 弱口令漏洞发现! 用户: {user} 密码: {pwd}")
                        print(f"    原因: {result['reason']}")
                        self.found_credentials.append({"username": user, "password": pwd, "type": "弱口令"})
                        found = True
                        break  # 发现一个就停止当前用户的测试
                except Exception as e:
                    print(f"[!] 请求失败: {str(e)}")
                    continue
            
            if found:  # 如果已经发现漏洞，停止测试其他用户
                break
              
        if not found:
            print("[+] 未发现弱口令漏洞")
        else:
            print(f"[+] 弱口令测试完成，发现 {len([c for c in self.found_credentials if c['type'] == '弱口令'])} 个漏洞")
        return found

    def test_arbitrary_user(self):
        """测试任意用户登录漏洞"""
        print("\n[*] 开始任意用户登录测试...")
        # 此测试通常依赖逻辑绕过，有时验证码不影响此逻辑（如修改返回包），但通常也受阻
        # 这里不做强制跳过，仅提示
        if self.has_captcha:
            print(f"[!] 提示: 存在验证码，可能会干扰任意用户登录的响应分析。")
        
        # 创建多个随机的不存在用户进行测试
        test_samples = []
        for i in range(5):  # 创建5个随机用户进行测试
            fake_user = f"nonexistent_user_{random.randint(1000, 9999)}_{i}"
            fake_pwd = f"fake_password_{random.randint(1000, 9999)}_{i}"
            test_samples.append((fake_user, fake_pwd))
        
        print(f"    将使用 {len(test_samples)} 个随机用户进行测试...")
        
        # 首先测试一个已知的错误凭据，获取错误页面的特征
        error_user = f"definitely_not_exists_{random.randint(1000, 9999)}"
        error_pwd = f"definitely_not_exists_{random.randint(1000, 9999)}"
        
        # 动态获取CSRF令牌
        csrf_token = self._dynamic_get_csrf_token()
        if not csrf_token:
            csrf_token = self.csrf_token
        
        error_data = {
            self.username_field: error_user,
            self.password_field: error_pwd
        }
        if self.csrf_field and csrf_token:
            error_data.update({self.csrf_field: csrf_token})
        
        try:
            error_response = self.session.post(self.login_action, data=error_data, timeout=10, verify=False)
            error_result = self._check_login_success_detailed(error_response)
            
            print(f"    错误凭据测试结果: {error_result['reason']}")
            
            # 测试多个随机用户
            arbitrary_login_found = False
            for i, (fake_user, fake_pwd) in enumerate(test_samples, 1):
                print(f"    [{i}/{len(test_samples)}] 测试随机用户: {fake_user}:{fake_pwd}")
                
                data = {
                    self.username_field: fake_user,
                    self.password_field: fake_pwd
                }
                if self.csrf_field and csrf_token:
                    data.update({self.csrf_field: csrf_token})
                
                response = self.session.post(self.login_action, data=data, timeout=10, verify=False)
                result = self._check_login_success_detailed(response)
                
                print(f"        随机用户测试结果: {result['reason']}")
                
                # 更严格的判断逻辑
                if error_result['success'] == False and result['success'] == True:
                    print(f"[!] 任意用户登录漏洞发现! 可使用任意用户名密码登录")
                    print(f"    错误凭据: 失败 ({error_result['reason']})")
                    print(f"    随机用户: 成功 ({result['reason']})")
                    arbitrary_login_found = True
                    break
                else:
                    # 进一步比较响应内容差异
                    error_content = error_response.text.lower()
                    random_content = response.text.lower()
                    
                    # 计算相似度（简单实现）
                    common_chars = set(error_content) & set(random_content)
                    similarity = len(common_chars) / max(len(set(error_content)), len(set(random_content)), 1)
                    
                    if similarity > 0.85:  # 如果相似度超过85%
                        print(f"[!] 可能存在任意用户登录漏洞!")
                        print(f"    响应内容相似度: {similarity:.2f}")
                        print(f"    两个请求的响应非常相似，可能未正确验证凭据")
                        arbitrary_login_found = True
                        break
                        
        except Exception as e:
            print(f"[!] 请求失败: {str(e)}")
            return False
          
        if not arbitrary_login_found:
            print("[+] 未发现任意用户登录漏洞")
        else:
            print(f"[+] 任意用户登录测试完成，发现潜在漏洞。")
        return arbitrary_login_found

    def test_user_enum(self):
        """测试用户枚举漏洞"""
        print("\n[*] 开始用户枚举测试...")
        # 验证码通常就是为了防止枚举，所以如果存在验证码，枚举大概率不可行
        if self.has_captcha:
            print(f"[!] 警告: 存在验证码，这通常是防止用户枚举的有效手段。")
            print("    继续测试可能导致IP被封禁。")
            choice = input("    是否继续? (y/n): ").lower()
            if choice != 'y':
                return False
        
        # 更全面的用户枚举测试样本
        test_users = [
            # 已知的可能存在的用户
            "admin", "administrator", "root", "Admin", "ROOT", "Admin123",
            # 常见用户名
            "test", "guest", "user", "demo", "demo1", "demo2", "testuser",
            # 系统用户
            "system", "service", "operator", "maint", "backup", "webadmin",
            # 随机生成的不存在用户
            "nonexistent_user_" + ''.join(random.choices('abcdefghijklmnopqrstuvwxyz0123456789', k=8)),
            "fake_user_" + ''.join(random.choices('abcdefghijklmnopqrstuvwxyz0123456789', k=6)),
            "unknown_user_" + ''.join(random.choices('abcdefghijklmnopqrstuvwxyz0123456789', k=5)),
            "not_real_user_" + ''.join(random.choices('abcdefghijklmnopqrstuvwxyz0123456789', k=4))
        ]
        
        base_pwd = "test_password_" + ''.join(random.choices('abcdefghijklmnopqrstuvwxyz0123456789', k=8))
        print(f"    将测试 {len(test_users)} 个用户...")
      
        responses = {}
        for i, user in enumerate(test_users, 1):
            print(f"    [{i}/{len(test_users)}] 测试用户: {user}")
            
            # 动态获取CSRF令牌
            csrf_token = self._dynamic_get_csrf_token()
            if not csrf_token:
                csrf_token = self.csrf_token
            
            data = {
                self.username_field: user,
                self.password_field: base_pwd
            }
            if self.csrf_field and csrf_token:
                data.update({self.csrf_field: csrf_token})
              
            try:
                start_time = time.time()
                response = self.session.post(self.login_action, data=data, timeout=10, verify=False)
                end_time = time.time()
                
                responses[user] = {
                    'status_code': response.status_code,
                    'content_length': len(response.content),
                    'response_time': end_time - start_time,
                    'content_hash': hashlib.md5(response.content).hexdigest(),
                    'headers': dict(response.headers),
                    'cookies': dict(response.cookies)
                }
            except Exception as e:
                print(f"[!] 请求失败: {str(e)}")
                continue
          
        # 分析响应差异
        if len(responses) < 2:
            print("[+] 响应样本不足，无法判断用户枚举漏洞")
            return False
          
        # 分类已知可能存在的用户和随机用户
        known_valid_users = ["admin", "administrator", "root", "Admin", "ROOT", "Admin123"]
        valid_users = [user for user in test_users if user in known_valid_users]
        invalid_users = [user for user in test_users if user not in known_valid_users]
        
        print(f"\n[+] 分析结果:")
        print(f"    - 已知可能存在的用户: {len(valid_users)} 个")
        print(f"    - 随机不存在的用户: {len(invalid_users)} 个")
        
        # 检查状态码差异
        if valid_users and invalid_users:
            valid_codes = [responses[user]['status_code'] for user in valid_users if user in responses]
            invalid_codes = [responses[user]['status_code'] for user in invalid_users if user in responses]
            
            if valid_codes and invalid_codes:
                if set(valid_codes) != set(invalid_codes):
                    print("[!] 用户枚举漏洞发现! 服务器响应状态码存在差异")
                    print(f"    - 有效用户响应码: {set(valid_codes)}")
                    print(f"    - 无效用户响应码: {set(invalid_codes)}")
                    return True
          
        # 检查响应长度差异
        if valid_users and invalid_users:
            valid_lengths = [responses[user]['content_length'] for user in valid_users if user in responses]
            invalid_lengths = [responses[user]['content_length'] for user in invalid_users if user in responses]
            
            if valid_lengths and invalid_lengths:
                avg_valid = sum(valid_lengths) / len(valid_lengths)
                avg_invalid = sum(invalid_lengths) / len(invalid_lengths)
                
                # 如果平均长度差异超过20%
                if abs(avg_valid - avg_invalid) / max(avg_valid, avg_invalid) > 0.2:
                    print("[!] 用户枚举漏洞发现! 服务器响应内容长度存在明显差异")
                    print(f"    - 有效用户平均响应长度: {int(avg_valid)}")
                    print(f"    - 无效用户平均响应长度: {int(avg_invalid)}")
                    return True
              
        # 检查响应时间差异
        if valid_users and invalid_users:
            valid_times = [responses[user]['response_time'] for user in valid_users if user in responses]
            invalid_times = [responses[user]['response_time'] for user in invalid_users if user in responses]
            
            if valid_times and invalid_times:
                avg_valid = sum(valid_times) / len(valid_times)
                avg_invalid = sum(invalid_times) / len(invalid_times)
                
                # 如果平均时间差异超过20%
                if abs(avg_valid - avg_invalid) / max(avg_valid, avg_invalid) > 0.2:
                    print("[!] 用户枚举漏洞发现! 服务器响应时间存在明显差异")
                    print(f"    - 有效用户平均响应时间: {avg_valid:.2f}秒")
                    print(f"    - 无效用户平均响应时间: {avg_invalid:.2f}秒")
                    return True
              
        print("[+] 未发现用户枚举漏洞")
        return False

    def check_dictionary_files(self):
        """检查字典文件状态"""
        print("\n[*] 检查字典文件状态...")
      
        # 检查rockyou.txt
        if os.path.exists(WEAK_PASS_PATH):
            print(f"[+] rockyou.txt 字典已就绪: {WEAK_PASS_PATH}")
            return True
        elif os.path.exists(WEAK_PASS_GZ_PATH):
            print(f"[!] rockyou.txt 字典未解压: {WEAK_PASS_GZ_PATH}")
            print(f"    请执行以下命令解压:")
            print(f"    sudo gzip -d {WEAK_PASS_GZ_PATH}")
            return False
        else:
            print(f"[!] rockyou.txt 字典文件缺失")
            print(f"    请安装wordlists包:")
            print(f"    sudo apt install wordlists")
            return False

    def extract_failure_strings(self, response_text):
        """从响应文本中提取可能的失败条件字符串"""
        failure_patterns = [
            r'登录失败', r'用户名或密码错误', r'账号不存在', r'密码错误', r'用户名错误',
            r'invalid', r'error', r'failed', r'incorrect', r'denied', r'not found', r'wrong',
            r'验证码错误', r'code error', r'captcha'
        ]
        found_strings = []
        for pattern in failure_patterns:
            matches = re.findall(pattern, response_text, re.IGNORECASE)
            if matches:
                found_strings.extend(matches)
        return list(set(found_strings))

    def analyze_login_response(self):
        """分析登录响应，帮助用户确定失败/成功条件"""
        print("\n[*] 分析登录响应...")
        print("="*60)
        print("【重要说明】失败条件字符串是Hydra判断登录是否成功的关键")
        print("  - 当响应中包含该字符串时，Hydra认为登录失败")
        print("  - 当响应中不包含该字符串时，Hydra认为登录成功")
        print("  - 例如：如果登录失败页面显示'登录失败'，则输入'登录失败'")
        print("="*60)
      
        # 测试无效登录
        fake_user = "nonexistent_user_" + ''.join(random.choices('abcdefghijklmnopqrstuvwxyz0123456789', k=8))
        fake_pwd = "fake_password_" + ''.join(random.choices('abcdefghijklmnopqrstuvwxyz0123456789', k=8))
      
        print(f"\n[+] 无效登录测试 (用户: {fake_user})")
        
        # 动态获取CSRF令牌
        csrf_token = self._dynamic_get_csrf_token()
        if not csrf_token:
            csrf_token = self.csrf_token
        
        data = {
            self.username_field: fake_user,
            self.password_field: fake_pwd
        }
        if self.csrf_field and csrf_token:
            data.update({self.csrf_field: csrf_token})
          
        try:
            response = self.session.post(self.login_action, data=data, timeout=10, verify=False)
          
            print(f"    - 状态码: {response.status_code}")
            print(f"    - 响应长度: {len(response.content)} 字节")
          
            # 提取可能的失败条件字符串
            failure_strings = self.extract_failure_strings(response.text)
          
            print("\n[+] 无效登录响应内容 (前1000字符):")
            print("-"*60)
            print(response.text[:1000])
            print("-"*60)
          
            # 检测重定向
            if response.history:
                print(f"\n[!] 检测到重定向:")
                for resp in response.history:
                    print(f"    - {resp.status_code} -> {resp.url}")
                print(f"    - 最终URL: {response.url}")
                self.redirect_url = response.url
          
            # 显示检测到的失败条件字符串
            if failure_strings:
                print("\n[!] 自动检测到可能的失败条件字符串:")
                for i, s in enumerate(failure_strings, 1):
                    print(f"    {i}. '{s}'")
              
                # 默认选择第一个
                default_failure = failure_strings[0]
                print(f"\n[+] 默认选择第一个字符串作为失败条件: '{default_failure}'")
                return default_failure
        except Exception as e:
            print(f"[!] 请求失败: {str(e)}")
            return None
      
        print("\n[+] 请根据以上响应内容确定:")
        print("    - 失败条件: 登录失败时响应中包含的唯一字符串")
        print("    - 成功条件: 登录成功时响应中包含的唯一字符串")
        print("    - 重定向条件: 登录成功时是否有重定向 (C=)")
      
        return None

    def build_hydra_command(self, threads=16):
        """构建Hydra命令"""
        print("\n[*] 构建Hydra命令...")
        
        # 验证码强阻断
        if self.has_captcha:
            print("\n" + "!"*60)
            print("[阻断] 检测到验证码机制！")
            print(f"特征: {self.captcha_type}")
            print("Hydra 不支持验证码绕过。暴力破解将无效。")
            print("建议：")
            print("1. 寻找验证码绕过漏洞（如验证码不刷新、前端验证等）")
            print("2. 使用支持验证码识别的高级工具（如Burp Suite插件）")
            print("!"*60)
            
            confirm = input("\n[?] 这是一个演示环境或你确定验证码已失效？强行生成命令？(yes/no): ").lower()
            if confirm != "yes":
                print("[-] 已取消 Hydra 命令生成。")
                return None
      
        # 检查字典文件
        if not self.check_dictionary_files():
            print("[!] 无法执行暴力破解，请先解压字典文件")
            return None
          
        # 检查用户名字典
        if not os.path.exists(USERNAMES_PATH):
            print(f"[!] 用户名字典文件缺失: {USERNAMES_PATH}")
            print("    请安装seclists包:")
            print("    sudo apt install seclists")
            return None
      
        # 分析登录响应并获取默认条件
        auto_condition = self.analyze_login_response()
      
        # 获取用户自定义条件
        print("\n[*] 设置Hydra检测条件:")
        print("1. 失败条件 (F=) - 响应中包含该字符串时认为登录失败")
        print("2. 成功条件 (S=) - 响应中包含该字符串时认为登录成功")
        print("3. 重定向条件 (C=) - 登录成功时有重定向")
        print("4. 自动选择 - 使用自动检测的条件")
      
        condition_type = input("\n[?] 选择检测条件类型 (1/2/3/4): ").strip()
      
        condition_param = ""
        if condition_type == "1":
            print("\n[+] 失败条件说明:")
            print("  例如：如果登录失败页面显示'用户名或密码错误'")
            print("  则输入: 用户名或密码错误")
            condition = input("[?] 请输入失败条件字符串: ").strip()
            if not condition and auto_condition:
                print(f"[!] 使用自动检测的失败条件: '{auto_condition}'")
                condition = auto_condition
            elif not condition:
                print("[!] 使用默认失败条件: '登录失败'")
                condition = "登录失败"
            condition_param = f"F={condition}"
        elif condition_type == "2":
            print("\n[+] 成功条件说明:")
            print(f"  检测到技术栈: {self.tech_stack}")
            print(f"  推荐{self.tech_stack}技术栈的成功条件字符串:")
            for i, s in enumerate(SUCCESS_STRINGS_BY_TECH[self.tech_stack], 1):
                print(f"    {i}. '{s}'")
            print("\n  请根据实际登录成功后的页面内容输入")
            condition = input("[?] 请输入成功条件字符串: ").strip()
            if not condition:
                print(f"[!] 使用默认成功条件: '{SUCCESS_STRINGS_BY_TECH[self.tech_stack][0]}'")
                condition = SUCCESS_STRINGS_BY_TECH[self.tech_stack][0]
            condition_param = f"S={condition}"
        elif condition_type == "3":
            if self.redirect_url:
                condition_param = "C="
                print(f"\n[+] 使用重定向条件: {condition_param}")
            else:
                print("[!] 未检测到重定向，无法使用重定向条件")
                return None
        elif condition_type == "4":
            # 自动选择最佳条件
            if self.redirect_url:
                condition_param = "C="
                print(f"\n[!] 自动选择重定向条件: {condition_param}")
            elif auto_condition:
                condition_param = f"F={auto_condition}"
                print(f"\n[!] 自动选择检测到的失败条件: {condition_param}")
            else:
                condition_param = f"S={SUCCESS_STRINGS_BY_TECH[self.tech_stack][0]}"
                print(f"\n[!] 自动选择{self.tech_stack}技术栈的成功条件: {condition_param}")
        else:
            print("[!] 使用默认失败条件: '登录失败'")
            condition_param = "F=登录失败"
      
        # 获取额外参数
        extra_params = input("\n[?] 是否需要添加额外参数? (例如: H=Cookie: session=xxx) (留空跳过): ").strip()
      
        # 解析表单提交路径，确保以斜杠开头
        parsed_action = urlparse(self.login_action)
        path = parsed_action.path
        if not path.startswith('/'):
            path = '/' + path
          
        # 构建模块选项
        module_options = f"{path}:{self.username_field}=^USER^&{self.password_field}=^PASS^:{condition_param}"
      
        # 添加CSRF令牌
        if self.csrf_field:
            module_options += f"&{self.csrf_field}=^PASS^"  # 这里需要特殊处理
        
        # 添加额外参数
        if extra_params:
            module_options += f":{extra_params}"
      
        # 构建Hydra命令
        service = f"{self.scheme}-post-form"
        cmd = [
            "hydra",
            "-I",  # 显示每次尝试的详细信息
            "-L", USERNAMES_PATH,
            "-P", WEAK_PASS_PATH,
            "-e", "ns",  # 尝试空密码和用户名作为密码
            "-f",       # 找到密码后停止
            "-t", str(threads),
            "-o", self.result_file,
            "-V",       # 详细模式
        ]
      
        # 添加端口参数（如果不是标准端口）
        if (self.scheme == 'https' and self.port != 443) or (self.scheme == 'http' and self.port != 80):
            cmd.extend(["-s", str(self.port)])
          
        cmd.extend([
            self.host,
            service,
            module_options
        ])
      
        # 添加忽略SSL证书选项（仅对https）
        if self.scheme == 'https':
            cmd.extend(["-k", "--ignore-cert"])
          
        print(f"\n[+] 构建的Hydra命令:")
        print("-"*80)
        print(" ".join(cmd))
        print("-"*80)
      
        return cmd

    def run_hydra_brute_force(self, threads=16):
        """调用Hydra进行暴力破解"""
        print("\n[*] 启动Hydra暴力破解...")
        
        # 验证码强阻断
        if self.has_captcha:
            print("\n" + "!"*60)
            print("[阻断] 检测到验证码机制！")
            print(f"特征: {self.captcha_type}")
            print("Hydra 无法自动识别验证码。暴力破解将无效。")
            print("建议：")
            print("1. 寻找验证码绕过漏洞（如验证码不刷新、前端验证等）")
            print("2. 使用支持验证码识别的高级工具（如Burp Suite插件）")
            print("!"*60)
            
            # 显示检测证据
            print("\n[检测证据回顾]:")
            for idx, evidence in enumerate(self.captcha_evidence, 1):
                print(f"    {idx}. {evidence}")
            
            force = input("\n[?] 这是一个演示环境或你确定验证码已失效？强制继续？(yes/no): ").lower()
            if force != "yes":
                print("[-] 已取消 Hydra 暴力破解。")
                return False
        
        cmd = self.build_hydra_command(threads)
        if not cmd:
            return False
      
        try:
            # 执行Hydra命令
            process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
          
            # 实时显示输出
            while True:
                output = process.stdout.readline()
                if output == '' and process.poll() is not None:
                    break
                if output:
                    print(output.strip())
          
            # 检查返回码
            return_code = process.poll()
            if return_code == 0:
                print("\n[+] Hydra执行完成")
            else:
                print(f"\n[!] Hydra执行失败，返回码: {return_code}")
                stderr = process.stderr.read()
                if stderr:
                    print(f"[ Hydra错误信息 ]\n{stderr}")
                return False
              
            # 检查结果
            if os.path.exists(self.result_file) and os.path.getsize(self.result_file) > 0:
                with open(self.result_file, 'r') as f:
                    results = f.read()
                  
                # 验证发现的凭据
                valid_credentials = []
                lines = results.split('\n')
                for line in lines:
                    if "host:" in line and "login:" in line and "password:" in line:
                        # 提取用户名和密码
                        host_match = re.search(r'host:\s+(\S+)', line)
                        login_match = re.search(r'login:\s+(\S+)', line)
                        pass_match = re.search(r'password:\s+(\S+)', line)
                      
                        if host_match and login_match and pass_match:
                            host = host_match.group(1)
                            username = login_match.group(1)
                            password = pass_match.group(1)
                          
                            # 验证凭据
                            print(f"\n[*] 验证凭据: {username}:{password}")
                            if self._verify_credentials(username, password):
                                print(f"[+] 凭据有效: {username}:{password}")
                                valid_credentials.append({"username": username, "password": password, "type": "暴力破解"})
                            else:
                                print(f"[!] 凭据无效: {username}:{password}")
              
                if valid_credentials:
                    self.found_credentials.extend(valid_credentials)
                    print("\n[!] 暴力破解成功! 发现有效凭据:")
                    for cred in valid_credentials:
                        print(f"    - {cred['username']}:{cred['password']}")
                    return True
                else:
                    print("\n[-] 未发现有效凭据")
                    return False
            else:
                print("[+] 暴力破解未发现有效凭据")
                return False
              
        except Exception as e:
            print(f"[!] Hydra执行失败: {str(e)}")
            return False

    def _verify_credentials(self, username, password):
        """验证凭据是否有效 - 使用更严格的验证"""
        try:
            # 创建一个新的session用于验证
            verify_session = requests.Session()
            verify_session.verify = False
            verify_session.headers.update({"User-Agent": random.choice(USER_AGENTS)})
            
            # 获取CSRF令牌
            csrf_token = self._dynamic_get_csrf_token()
            if not csrf_token:
                csrf_token = self.csrf_token
            
            data = {
                self.username_field: username,
                self.password_field: password
            }
            if self.csrf_field and csrf_token:
                data.update({self.csrf_field: csrf_token})
              
            response = verify_session.post(self.login_action, data=data, timeout=10, verify=False)
            result = self._check_login_success_detailed(response, self.base_response_cache)
            
            return result['success']
          
        except Exception as e:
            print(f"[!] 验证失败: {str(e)}")
            return False

def generate_report(found_credentials, target_url, captcha_info=None):
    """生成测试报告"""
    timestamp = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
    report_file = f"login_test_report_{datetime.datetime.now().strftime('%Y%m%d_%H%M%S')}.txt"
  
    with open(report_file, 'w', encoding='utf-8') as f:
        f.write(f"登录界面渗透测试报告\n")
        f.write(f"{'='*50}\n")
        f.write(f"目标URL: {target_url}\n")
        f.write(f"测试时间: {timestamp}\n")
        f.write(f"{'='*50}\n\n")
        
        # 添加验证码检测信息
        if captcha_info:
            f.write(f"验证码检测信息:\n")
            f.write(f"{'-'*50}\n")
            f.write(f"检测到验证码: 是\n")
            f.write(f"证据数量: {len(captcha_info['evidence'])}\n")
            f.write(f"证据清单:\n")
            for idx, evidence in enumerate(captcha_info['evidence'], 1):
                f.write(f"  {idx}. {evidence}\n")
            f.write(f"\n{'='*50}\n\n")
      
        if found_credentials:
            f.write(f"发现漏洞: {len(found_credentials)} 个\n")
            f.write(f"{'-'*50}\n")
          
            for i, cred in enumerate(found_credentials, 1):
                f.write(f"{i}. 漏洞类型: {cred['type']}\n")
                f.write(f"   用户名: {cred['username']}\n")
                f.write(f"   密码: {cred['password']}\n")
                f.write(f"{'-'*50}\n")
        else:
            f.write("未发现明显安全漏洞\n")
      
        f.write(f"\n{'='*50}\n")
        f.write("测试完成\n")
  
    print(f"\n[+] 测试报告已保存至: {report_file}")
    return report_file

def main():
    """主函数"""
    print("""
    ===========================================
    |       登录界面渗透测试工具 v2.3        |
    |        Kali Linux 2025.2 专用         |
    |    增强：验证码检测证据详细展示        |
    ===========================================
    示例:
      hydra -L users.txt -P passwords.txt -e ns -f -t 16 -o results.txt \\
        -s 4433 example.com https-post-form \\
        "/login:username=^USER^&password=^PASS^:S=dashboard" \\
        -k --ignore-cert
        """)
  
    # 获取目标URL
    target_url = input("\n[+] 请输入目标登录页面URL (例如: https://example.com:4433/login): ").strip()
  
    if not target_url.startswith(('http://', 'https://')):
        target_url = 'https://' + target_url  # 默认使用HTTPS
      
    # 初始化测试器
    tester = LoginTester(target_url)
  
    # 执行基础测试
    vulnerabilities = []
    print("\n[*] 开始执行安全测试...")
    
    print("\n" + "="*60)
    print("1. 空口令测试")
    print("="*60)
    if tester.test_empty_password():
        vulnerabilities.append("空口令漏洞")
    
    print("\n" + "="*60)
    print("2. 弱口令测试")
    print("="*60)
    if tester.test_weak_passwords():
        vulnerabilities.append("弱口令漏洞")
    
    print("\n" + "="*60)
    print("3. 任意用户登录测试")
    print("="*60)
    if tester.test_arbitrary_user():
        vulnerabilities.append("任意用户登录漏洞")
    
    print("\n" + "="*60)
    print("4. 用户枚举测试")
    print("="*60)
    if tester.test_user_enum():
        vulnerabilities.append("用户枚举漏洞")
  
    # 执行暴力破解
    print("\n" + "="*60)
    print("5. 暴力破解测试")
    print("="*60)
    
    if tester.has_captcha:
        print(f"[!] 系统检测到验证码: {tester.captcha_type}")
        print("[!] 不建议进行暴力破解。")
    
    brute_force_choice = input("\n[?] 是否尝试执行暴力破解? (y/n): ").lower()
    if brute_force_choice == 'y':
        if tester.run_hydra_brute_force():
            vulnerabilities.append("暴力破解成功")
  
    # 准备验证码信息用于报告
    captcha_info = None
    if tester.has_captcha:
        captcha_info = {
            'detected': True,
            'evidence': tester.captcha_evidence
        }
    
    # 生成报告
    report_file = generate_report(tester.found_credentials, target_url, captcha_info)
  
    # 输出总结报告
    print("\n" + "="*60)
    print("渗透测试总结报告")
    print("="*60)
  
    if vulnerabilities:
        print("\n[!] 发现以下安全漏洞:")
        for i, vuln in enumerate(vulnerabilities, 1):
            print(f"    {i}. {vuln}")
        print(f"\n[!] 共发现 {len(vulnerabilities)} 个安全漏洞!")
        print("[!] 建议立即修复上述漏洞!")
    else:
        print("\n[+] 未发现明显安全漏洞")
    
    # 显示验证码检测总结
    if tester.has_captcha:
        print("\n[!] 验证码检测总结:")
        print(f"    - 检测到验证码: 是")
        print(f"    - 证据数量: {len(tester.captcha_evidence)} 个")
        print(f"    - 主要特征: {tester.captcha_type}")
        print(f"    - 影响: 自动化暴力破解极大概率失败")
      
    print(f"[*] 测试完成，详细结果已保存至: {report_file}")
    print("="*60)

if __name__ == "__main__":
    main()
